/**************************************************************************************************
 
  Phyplus Microelectronics Limited confidential and proprietary. 
  All rights reserved.

  IMPORTANT: All rights of this software belong to Phyplus Microelectronics 
  Limited ("Phyplus"). Your use of this Software is limited to those 
  specific rights granted under  the terms of the business contract, the 
  confidential agreement, the non-disclosure agreement and any other forms 
  of agreements as a customer or a partner of Phyplus. You may not use this 
  Software unless you agree to abide by the terms of these agreements. 
  You acknowledge that the Software may not be modified, copied, 
  distributed or disclosed unless embedded on a Phyplus Bluetooth Low Energy 
  (BLE) integrated circuit, either as a product or is integrated into your 
  products.  Other than for the aforementioned purposes, you may not use, 
  reproduce, copy, prepare derivative works of, modify, distribute, perform, 
  display or sell this Software and/or its documentation for any purposes.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  PHYPLUS OR ITS SUBSIDIARIES BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
  
**************************************************************************************************/


#include <string.h>
#include "types.h"
#include "att.h"
#include "peripheral.h"
#include "uart.h"
#include "OSAL.h"
#include "bcomdef.h"
#include "pwrmgr.h"
#include "bleuart.h"
#include "error.h"
#include "log.h"
#include "bleuart_protocol.h"
#include "bleuart.h"
#include "bleuart_service.h"
#include "bleuart_at_cmd.h"

#define UART_RX_BUF_SIZE  512
#define UART_TX_BUF_SIZE  512

#define UART_RX_PREAMBLE_CODE  0xfe
#define UART_RX_PREAMBLE_SIZE  16
//#define AT_CMD_PT_LEN_MAX      20

enum{
  BUP_RX_ST_IDLE = 0,
  BUP_RX_ST_DELAY_SLOT,
  BUP_RX_ST_SENDING
};

enum{
  BUP_TX_ST_IDLE = 0,
  BUP_TX_ST_DELAY_SLOT,
  BUP_TX_ST_SENDING
};

typedef struct{
  bool    conn_state;
  //uart_rx
  uint8_t rx_state;
  uint8_t rx_size;
  uint8_t rx_offset;
  uint8_t rx_buf[UART_RX_BUF_SIZE];

  //uart tx
  uint8_t tx_state;
  uint8_t tx_size;
  uint8_t tx_buf[UART_TX_BUF_SIZE];


  uint8_t hal_uart_rx_size;
  uint8_t hal_uart_rx_buf[UART_RX_BUF_SIZE];
  uint8_t hal_uart_tx_buf[UART_TX_BUF_SIZE];
  
}BUP_ctx_t;

BUP_ctx_t mBUP_Ctx = {0};

//uint8_t gRxIdleTimeout = 3;

//uint8_t ptcmdstr[AT_CMD_PT_LEN_MAX] = {0};
//uint8_t ptcmdlen = 0;
uint8_t ptcmd[] = "at+reset";
//uint8_t at_flag  = 0;

int BUP_disconnect_handler(void);


static void uartrx_timeout_timer_start(void)
{
  LOG("BLE start Timer\n");
  osal_start_timerEx(bleuart_TaskID, BUP_OSAL_EVT_UART_TO_TIMER, 10); 
}

static void uartrx_timeout_timer_stop(void)
{
  osal_stop_timerEx(bleuart_TaskID, BUP_OSAL_EVT_UART_TO_TIMER);

}

/*uint8_t get_rx_idle_timeout(void)
{
	return gRxIdleTimeout;
}

void set_rx_idle_timeout(uint8_t idleTimeout)
{
	gRxIdleTimeout = idleTimeout;
}
*/
static void uartrx_idle_timer_start(void)
{
	uint32_t m_aust = at_get_auto_slp_time();
  LOG("BLE start Rx idle Timer\n");
  //osal_start_timerEx(bleuart_TaskID, BUP_OSAL_EVT_RX_IDLE_TIMER, m_aust * 1000); 
	osal_start_timerEx(bleuart_TaskID, BUP_OSAL_EVT_AT_AUTO_SLEEP, m_aust * 1000);
}

static void uartrx_idle_timer_stop(void)
{
  //osal_stop_timerEx(bleuart_TaskID, BUP_OSAL_EVT_RX_IDLE_TIMER);
  osal_stop_timerEx(bleuart_TaskID, BUP_OSAL_EVT_AT_AUTO_SLEEP);
}

//timer for ble send delay slot
//static void tx_start_timer(uint16_t timeout)
//{
//  LOG("BLE start Timer\n");
//  osal_start_timerEx(bleuart_TaskID, BUP_OSAL_EVT_BLE_TIMER, timeout);
//}

//timer for uart send delay slot
//static void rx_start_timer(uint16_t timeout)
//{
//  LOG("uart start Timer\n");
//  osal_start_timerEx(bleuart_TaskID, BUP_OSAL_EVT_UARTRX_TIMER, timeout);
//}

void uart_evt_hdl(uart_Evt_t* pev)
{
  BUP_ctx_t* pctx = & mBUP_Ctx;
  uint16_t conn_hdl;
	uint8_t i = 0;
	bool skip_flag = false;
  GAPRole_GetParameter( GAPROLE_CONNHANDLE, &conn_hdl);
  if(conn_hdl == INVALID_CONNHANDLE){ 
    BUP_disconnect_handler(); 
    return;
  }
  switch(pev->type){
    case  UART_EVT_TYPE_RX_DATA:
		case  UART_EVT_TYPE_RX_DATA_TO:
//			if(!mBUP_Ctx.conn_state){ // Not in connected status, handle at cmds only.
//				uartrx_timeout_timer_stop();
//        osal_memcpy((cmdstr + cmdlen), pev->data, pev->len);
//        cmdlen += pev->len;
//        osal_set_event( bleuart_TaskID, BUP_OSAL_EVT_AT_UART_RX_CMD );
//				break;
//			}
//			else{		
				if((pctx->hal_uart_rx_size + pev->len)>=UART_RX_BUF_SIZE)
					break;
				// Skip preamble data.
/*				if(at_get_pw_mode() && ((uint8_t)(UART_RX_PREAMBLE_SIZE/2) < pev->len) && (pev->len < UART_RX_PREAMBLE_SIZE) ){
					len = (uint8_t)(UART_RX_PREAMBLE_SIZE/2);
					// Skip first half data to avoid dis-ordered code. ?? To be verified.
					skip_flag = true;
					for(i=0; i<len; i++){ 
						if(pev->data[pev->len - i - 1] != UART_RX_PREAMBLE_CODE){
							skip_flag = false;
							break;
						}
						else
							continue;
					}
				}
*/
				if(at_get_pw_mode()){
					uartrx_idle_timer_stop();
					uartrx_idle_timer_start();
					if((pev->len == 1) && (pctx->hal_uart_rx_size == 0)){ // preamble code is the only data for this Rx data 'packet', skip it.
						if(pev->data[0] == UART_RX_PREAMBLE_CODE){
							skip_flag = true;
						}
					}
					else if((1 < pev->len) && (pev->len < UART_RX_PREAMBLE_SIZE)){ // skip the 'packets' ended with 2 bytes of preamble code too.
						skip_flag = true;
						for(i=0; i<2; i++){ 
							if(pev->data[pev->len - i - 1] != UART_RX_PREAMBLE_CODE){
								skip_flag = false;
								break;
							}
							else
								continue;
						}
					}
				}
					
				uartrx_timeout_timer_stop();
				uartrx_timeout_timer_start();
				
				// Re-start uartrx timer even if it's skipped. ?? To be verified.
				if(skip_flag)
					break;
				
				// Identify AT cmd in PT mode.
/*				switch(pev->len){
					case 1:{
						if(pctx->hal_uart_rx_size == 0 || at_flag == 1){
							switch(ptcmdlen){
								case 0:
									if(pev->data[ptcmdlen] == ptcmd[ptcmdlen]){
										at_flag = 1;
										skip_flag = true;
										ptcmdstr[ptcmdlen] = ptcmd[ptcmdlen];
										ptcmdlen++;
									}
									break;
								case 1:
									if(pev->data[ptcmdlen] == ptcmd[ptcmdlen]){
										at_flag = 1;
										skip_flag = true;
										ptcmdstr[ptcmdlen] = ptcmd[ptcmdlen];
										ptcmdlen++;
									}
									else{
										at_flag = 0;
									}
									break;
								case 2:
									if(pev->data[ptcmdlen] == ptcmd[ptcmdlen]){
										at_flag = 1;
										skip_flag = true;
										ptcmdstr[ptcmdlen] = ptcmd[ptcmdlen];
										ptcmdlen++;
									}
									else{ // run at cmd and clear the buffer.
										at_flag = 0;
										at_at(0,NULL);
										ptcmdstr[0] = 0;
										ptcmdlen = 0;
									}
									break;
								case 3:
								case 4:
								case 5:
								case 6:
								case 7:
									if(pev->data[ptcmdlen] == ptcmd[ptcmdlen]){
										at_flag = 1;
										skip_flag = true;
										ptcmdstr[ptcmdlen] = ptcmd[ptcmdlen];
										ptcmdlen++;
									}
									else{
										at_flag = 0;
									}
									break;
								default:
									break;
							}
						}
						break;
					}
					case 2:{
						if(pctx->hal_uart_rx_size == 0){
							if(pev->data[0] == 'a' && \
								 pev->data[1] == 't'){
								skip_flag = true;
								at_at(0,NULL);
							}
						}
						break;
					}
					case 8:{
						if(pctx->hal_uart_rx_size == 0){
							skip_flag = true;
							for(int i=0; i<8; i++){
								if(pev->data[i] != ptcmd[i]){
									skip_flag = false;
									break;
								}
								else
									continue;
							}
							if(skip_flag)
								at_reset(0,NULL);
						}
						break;
					}
					default:
						break;	
			  }

				if(skip_flag)
					break;
				
				if(ptcmdlen != 0){ // restore the data skipped before.
 				  memcpy(pctx->hal_uart_rx_buf + pctx->hal_uart_rx_size, ptcmdstr, ptcmdlen);
				  pctx->hal_uart_rx_size += ptcmdlen;
					memset(ptcmdstr, 0, ptcmdlen);
					ptcmdlen = 0;
					at_flag = 0;
				}
*/					
				memcpy(pctx->hal_uart_rx_buf + pctx->hal_uart_rx_size, pev->data, pev->len);
				pctx->hal_uart_rx_size += pev->len;
				break;
		case  UART_EVT_TYPE_TX_COMPLETED:
//			hal_gpio_write(P20,1);
//			hal_gpio_write(P20,0);
			osal_set_event(bleuart_TaskID, BUP_OSAL_EVT_UART_TX_COMPLETE);
			break;
		default:
			break;
  }
}

void gpio_sleep_handle()
{
	  //hal_gpio_write(FLOW_CTRL_IO_BLE_TX,1);
	  //hal_gpio_write(FLOW_CTRL_IO_BLE_TX,0);
	
		hal_gpio_pin_init(FLOW_CTRL_IO_HOST_WAKEUP, IE);
		hal_gpio_pin_init(P9, IE);
		hal_gpio_pull_set(P9, FLOATING);	  
		hal_gpio_fmux(FLOW_CTRL_IO_HOST_WAKEUP, Bit_DISABLE);      //enable fullmux fuction; enable or disable 
		hal_gpio_wakeup_set(FLOW_CTRL_IO_HOST_WAKEUP, NEGEDGE); //  NEGEDGE
}

void gpio_wakeup_handle()
{
	int i,flag = 0;
	uint8_t ret = 0;
	
	hal_gpio_pin_init(FLOW_CTRL_IO_HOST_WAKEUP, IE);
	
	if(hal_pwrmgr_is_lock(MOD_USR1) == FALSE)
	{
		for(i = 0; i< 100; i++){
		  if(hal_gpio_read(P10)==0)
		  {
			  //hal_gpio_write(P14,1);
				//hal_gpio_write(P14,0);
			  flag = TRUE;
			  break;
		  }
		}
		
		if( flag )
		{
			for(i = 0; i< 800; i++)
			{
				if(hal_gpio_read(P10)==1)
				{
			    //hal_gpio_write(P15,1);
				  //hal_gpio_write(P15,0);
					hal_pwrmgr_lock(MOD_USR1);
					at_initialize_fs();  // initialize the fs
					
					ret = at_snv_read_flash(); // re-read data from flash in case wakeup
					if(SUCCESS != ret){  
						at_default(0,NULL);
					}

					if(get_uart_at_mod()){
						uint32_t m_auto_slp_time = at_get_auto_slp_time();
						at_Init();
			      AP_UART0->LCR = 0x0b; //8bit, 1 stop odd parity TEST
						osal_start_timerEx( bleuart_TaskID, BUP_OSAL_EVT_AT_AUTO_SLEEP, m_auto_slp_time * 1000 );
					}
					else{
						BUP_connect_handler();
			      AP_UART0->LCR = 0x1b; //8bit, 1 stop odd parity TEST
						BUP_init();
					}
					break;
				}
			}
		}
	}

}


int BUP_data_BLE_to_uart_completed(void)
{
  BUP_ctx_t* pctx = &mBUP_Ctx;
  if(pctx->tx_size){
    BUP_data_BLE_to_uart_send();
    return PPlus_SUCCESS;
  }
  //case no data in buffer
  pctx->tx_state = BUP_TX_ST_IDLE;
  return PPlus_SUCCESS;
}

int BUP_data_BLE_to_uart_send(void)
{
  BUP_ctx_t* pctx = &mBUP_Ctx;

  if(pctx->tx_state != BUP_TX_ST_IDLE && pctx->tx_size)
  {
		// run at cmd.
		if((pctx->tx_size) == 2 && \
			 (pctx->tx_buf[0] == 'a') && \
		   (pctx->tx_buf[1] == 't')){
			at_at(0,NULL);
			pctx->tx_state = BUP_TX_ST_IDLE;
			pctx->tx_size = 0;
			return PPlus_SUCCESS;
		}
		// run at+reset cmd.
		else if(pctx->tx_size == 8){
			bool flag = true;
			for(int i=0; i<8; i++){
				if(pctx->tx_buf[i] != ptcmd[i]){
					flag = false;
					break;
				}
				else
					continue;
			}
			if(flag)
				at_reset(0,NULL);
		}

    hal_uart_send_buff(pctx->tx_buf, pctx->tx_size);
    pctx->tx_size = 0;
    pctx->tx_state = BUP_TX_ST_SENDING;
    return PPlus_SUCCESS;
  }

  LOG("BUP_data_BLE_to_uart_send: incorrect state\n");
  return PPlus_ERR_INVALID_STATE;
}

int BUP_data_BLE_to_uart(uint8_t* pdata, uint8_t size)
{
  BUP_ctx_t* pctx = &mBUP_Ctx;
	//AT_LOG("t_1");
  switch(pctx->tx_state){
  case BUP_TX_ST_IDLE:
		if((pctx->tx_size + size)>=UART_TX_BUF_SIZE) // data overflow. 0304
		{
			//hal_gpio_write(P20,1);
			//hal_gpio_write(P20,0);
			break;
		}
		HAL_ENTER_CRITICAL_SECTION();
    memcpy(pctx->tx_buf + pctx->tx_size, pdata, size);
    pctx->tx_size += size;
    pctx->tx_state = BUP_TX_ST_DELAY_SLOT;
		HAL_EXIT_CRITICAL_SECTION();
    //tx_start_timer(1);  //1ms delay slot
	  BUP_data_BLE_to_uart_send();
    break;
  case BUP_TX_ST_DELAY_SLOT:
  case BUP_TX_ST_SENDING:
  {
		if((pctx->tx_size + size)>=UART_TX_BUF_SIZE) // data overflow. 0304
		{
			//hal_gpio_write(P18,1);
			//hal_gpio_write(P18,0);
			break;
		}
    HAL_ENTER_CRITICAL_SECTION();
    memcpy(pctx->tx_buf + pctx->tx_size, pdata, size);
    pctx->tx_size += size;
		HAL_EXIT_CRITICAL_SECTION();
    break;
  }
  default:
    mBUP_Ctx.tx_state = BUP_TX_ST_IDLE;
	  mBUP_Ctx.tx_size = 0;
    break;
  }
  return PPlus_SUCCESS;
}


int BUP_data_uart_to_BLE_send(void)
{
  BUP_ctx_t* pctx = &mBUP_Ctx;
//  bool start_flg = FALSE;
	//AT_LOG("r_1\n");
  if(pctx->rx_state != BUP_RX_ST_IDLE && pctx->rx_size)
  {
		// run at cmd.
		if((pctx->rx_size) == 2 && \
			 (pctx->rx_buf[0] == 'a') && \
		   (pctx->rx_buf[1] == 't')){
			at_at(0,NULL);
			pctx->rx_state = BUP_RX_ST_IDLE;
			pctx->rx_offset = 0;
			pctx->rx_size = 0;
			if(at_get_pw_mode()){
				uartrx_idle_timer_stop();
				uartrx_idle_timer_start();
			}
			return PPlus_SUCCESS;
		}
		// run at+reset cmd.
		else if(pctx->rx_size == 8){
			bool flag = true;
			for(int i=0; i<8; i++){
				if(pctx->rx_buf[i] != ptcmd[i]){
					flag = false;
					break;
				}
				else
					continue;
			}
			if(flag)
				at_reset(0,NULL);
		}
		
    if(bleuart_NotifyIsReady() == FALSE){
			BUP_connect_handler();
      return PPlus_ERR_BLE_NOT_READY;
		}

    if(pctx->rx_state == BUP_RX_ST_DELAY_SLOT){
      //start_flg = TRUE;
      pctx->rx_state = BUP_RX_ST_SENDING;
    }
    
    while(1){
      uint8_t size =0;
      bStatus_t ret = 0;
      attHandleValueNoti_t notify_data={0};
			
			HAL_ENTER_CRITICAL_SECTION();
      size = ((pctx->rx_size - pctx->rx_offset) > 20) ? 20 : pctx->rx_size - pctx->rx_offset; //ATT_MTU_SIZE  20  192
      memcpy(notify_data.value,pctx->rx_buf + pctx->rx_offset, size);
      notify_data.len = size;
			HAL_EXIT_CRITICAL_SECTION();
      
      ret = bleuart_Notify(gapConnHandle, &notify_data, bleuart_TaskID);
			//AT_LOG("bleuart_Notify: %d, %d, %d\n", ret,pctx->rx_offset, pctx->rx_size);
      if(ret == SUCCESS){
        pctx->rx_offset += size;
      }
      else
      {
        if(ret == MSG_BUFFER_NOT_AVAIL){
//          if(start_flg){
//						//AT_LOG("=NY1:");
//            rx_start_timer(1);
//          }
//          else
//          {
//						//AT_LOG("=NY2:");
//            rx_start_timer(bleuart_conn_interval()-1);
//          }
          return PPlus_SUCCESS;
        }
        else
        {
          return PPlus_ERR_BUSY;
        }
      }
      
      if(pctx->rx_offset == pctx->rx_size){
        LOG("Success\n");
        pctx->rx_state = BUP_RX_ST_IDLE;
        pctx->rx_offset = 0;
        pctx->rx_size = 0;
				if(at_get_pw_mode()){
					uartrx_idle_timer_stop();
					uartrx_idle_timer_start();
				}
        return PPlus_SUCCESS;
      }

    }
  
  }

  LOG("BUP_data_uart_to_BLE_send: incorrect state\n");
  return PPlus_ERR_INVALID_STATE;
}

int BUP_data_uart_to_BLE(void)
{
  BUP_ctx_t* pctx = &mBUP_Ctx;
  //AT_LOG("BUP_data_uart_to_BLE\n");
  if(pctx->conn_state == FALSE){
    pctx->rx_size = 0;
    pctx->rx_offset = 0;
    pctx->hal_uart_rx_size = 0;
    return PPlus_ERR_INVALID_STATE;
  }

//  if(pctx->rx_offset != 0){
//    return PPlus_ERR_BUSY;
//  }
	if((pctx->rx_size + pctx->hal_uart_rx_size) >= UART_TX_BUF_SIZE) // skip over-size data
  {
     return PPlus_ERR_DATA_SIZE;
  }		
	HAL_ENTER_CRITICAL_SECTION();
  memcpy(pctx->rx_buf + pctx->rx_size, pctx->hal_uart_rx_buf, pctx->hal_uart_rx_size);
  pctx->rx_size += pctx->hal_uart_rx_size;
  
	pctx->hal_uart_rx_size = 0;
	HAL_EXIT_CRITICAL_SECTION();
	
  switch(pctx->rx_state){
  case BUP_RX_ST_IDLE:
    pctx->rx_state = BUP_RX_ST_DELAY_SLOT;
    //rx_start_timer(1);  //1ms delay slot
	  BUP_data_uart_to_BLE_send();
    break;
  case BUP_RX_ST_DELAY_SLOT:
  case BUP_RX_ST_SENDING:
	  BUP_data_uart_to_BLE_send();
    break;
  default:
    //drop data
    LOG("BUP_data_uart_to_BLE error st\n");
    pctx->rx_size = 0;
    pctx->rx_offset = 0;
    pctx->hal_uart_rx_size = 0;
    return PPlus_ERR_INVALID_STATE;
  }
  return PPlus_SUCCESS;

}

int BUP_disconnect_handler(void)
{
  memset(&mBUP_Ctx, 0, sizeof(mBUP_Ctx));
//  hal_gpio_write(FLOW_CTRL_IO_BLE_CONNECTION, 0);
	if(at_get_pw_mode()){
		uartrx_idle_timer_stop();
	}
	return PPlus_SUCCESS;
}

int BUP_connect_handler(void)
{
    memset(&mBUP_Ctx, 0, sizeof(mBUP_Ctx));
    mBUP_Ctx.conn_state = TRUE;
//    hal_gpio_write(FLOW_CTRL_IO_BLE_CONNECTION, 0);
	  if(at_get_pw_mode()){
			uartrx_idle_timer_stop();
			uartrx_idle_timer_start();
			if(hal_pwrmgr_is_lock(MOD_USR1) == FALSE){
				hal_pwrmgr_lock(MOD_USR1);
			}
		}
  	return PPlus_SUCCESS;
}

int BUP_init()
{
  BUP_ctx_t* pctx = &mBUP_Ctx;
  uart_Cfg_t cfg = {
  .tx_pin = P9,
  .rx_pin = P10,
  .rts_pin = GPIO_DUMMY,
  .cts_pin = GPIO_DUMMY,
  .baudrate = 115200,
  .use_fifo = TRUE,
  .hw_fwctrl = FALSE,
  .use_tx_buf = TRUE,
  .parity     = FALSE,
  .evt_handler = uart_evt_hdl,
  };
	
	cfg.baudrate = at_get_baudrate();
	
	hal_uart_deinit();
  hal_uart_init(cfg);
	
#ifdef BLEUART_DEDICATE
	switch(at_get_uart_parity()){
		case (uint32_t)UART_PARITY_O:
			AP_UART0->LCR = 0x0b; //8bit, 1 stop odd parity
		  break;
		case (uint32_t)UART_PARITY_E:
			AP_UART0->LCR = 0x1b; //8bit, 1 stop even parity
		  break;
    default:
      break;			
	}
#endif
	
  hal_uart_set_tx_buf(pctx->hal_uart_tx_buf, UART_TX_BUF_SIZE);

//#ifndef BLEUART_DEDICATE	// 0310
//	AT_LOG("PT Mod\n");
//#endif
	
  return PPlus_SUCCESS;
}


